package raytracing;

import javax.swing.*;


import rasterization.Rasterizer;
import rendering.RaycastingRenderer;
import scene.Scene;
import scene.shape.BoundingBox;
import util.CgPanel;
import util.parsing.SceneBuilder;

import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;


public class RayCastingDemo implements MouseListener, KeyListener, MouseMotionListener, FocusListener {
	
	public static final int PANEL_WIDTH  = 800,
							PANEL_HEIGHT = 800;

public static void main(String[] args) {
	new RayCastingDemo();
}

private JFrame frame;
private CgPanel panel;
private Scene scene;
private RaycastingRenderer renderer;
private Rasterizer rasterizer;
private List<Ray> rays = new ArrayList<Ray>();
private BoundingBox bbox;

int oldX, oldY;

private Vector<Integer> keysDown = new Vector<Integer>();
/**
 * 
 * Build a scene from an XML file and raytrace it
 */
public RayCastingDemo() {
	try {
		SceneBuilder sceneBuilder = new SceneBuilder();
		scene = sceneBuilder.loadScene("var/XML/table.sdl");
	}
	catch (Exception e) {
		e.printStackTrace();
	}


	panel = new CgPanel();
	panel.addMouseListener(this);
	panel.addMouseMotionListener(this);
	frame = new JFrame();
	frame.addWindowListener(new WindowAdapter(){
		  	public void windowClosing(WindowEvent we){
		  		System.exit(0);
		  	}
		  });
	frame.addKeyListener(this);
	frame.getContentPane().add(panel);
	frame.setSize(PANEL_WIDTH,PANEL_HEIGHT);
	frame.setVisible(true);
	bbox = BoundingBox.computeBoundingBox(scene.getPrimitives());
	renderer = new RaycastingRenderer(scene, panel);
}

public void mousePressed(MouseEvent e) { }
public void mouseClicked(MouseEvent e) {
	if(e.getButton() == MouseEvent.BUTTON3){
		System.out.println("Mouse clicked at (" + e.getX() + ", " + e.getY() + ")");
		rasterizer.resetZBuffer();
		rasterizer.renderWireFrame(bbox);
		for(Ray ray : rays){
			HitRecord intersection = scene.hitScene(ray, 0, 600);
			rasterizer.drawWorldSpaceLine(ray.getOrigin(), intersection.getIntersectionPoint());
		}
		
	}else if(e.getButton() == MouseEvent.BUTTON2){
		Ray r = scene.getCurrentCamera().generatePerspectiveRay(e.getX(), panel.getHeight() - e.getY());
		rays.add(r);
	}
	else
		drawPixels();
	}
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseReleased(MouseEvent e) {oldX = 0; oldY = 0;}

public void drawPixels() {
	renderer.render(8);	
}

@Override
public void keyPressed(KeyEvent e) {
	if((e.getKeyCode() == KeyEvent.VK_Q ||
			e.getKeyCode() == KeyEvent.VK_D ||
			e.getKeyCode() == KeyEvent.VK_Z ||
			e.getKeyCode() == KeyEvent.VK_S ||
			e.getKeyCode() == KeyEvent.VK_SPACE ||
			e.getKeyCode() == KeyEvent.VK_X) && !keysDown.contains(e.getKeyCode())){
		keysDown.add(e.getKeyCode());
	}
	if(keysDown.size() >= 1){
		if(keysDown.contains(KeyEvent.VK_Q)){
			scene.getCurrentCamera().setEye(scene.getCurrentCamera().getEye().add(scene.getCurrentCamera().getU().scale(-0.1F)));
			scene.getCurrentCamera().setLookat(scene.getCurrentCamera().getLookat().add(scene.getCurrentCamera().getU().scale(-0.1F)));
		}
		if(keysDown.contains(KeyEvent.VK_D)){
			scene.getCurrentCamera().setEye(scene.getCurrentCamera().getEye().add(scene.getCurrentCamera().getU().scale(0.1F)));
			scene.getCurrentCamera().setLookat(scene.getCurrentCamera().getLookat().add(scene.getCurrentCamera().getU().scale(0.1F)));
		}if(keysDown.contains(KeyEvent.VK_Z)){
			scene.getCurrentCamera().setEye(scene.getCurrentCamera().getEye().add(scene.getCurrentCamera().getW().scale(-0.1F)));
			scene.getCurrentCamera().setLookat(scene.getCurrentCamera().getLookat().add(scene.getCurrentCamera().getW().scale(-0.1F)));
		}
		if(keysDown.contains(KeyEvent.VK_S)){
			scene.getCurrentCamera().setEye(scene.getCurrentCamera().getEye().add(scene.getCurrentCamera().getW().scale(0.1F)));
			scene.getCurrentCamera().setLookat(scene.getCurrentCamera().getLookat().add(scene.getCurrentCamera().getW().scale(0.1F)));
		}
		if(keysDown.contains(KeyEvent.VK_SPACE)){
			scene.getCurrentCamera().setEye(scene.getCurrentCamera().getEye().add(scene.getCurrentCamera().getV().scale(0.1F)));
			scene.getCurrentCamera().setLookat(scene.getCurrentCamera().getLookat().add(scene.getCurrentCamera().getV().scale(0.1F)));
		}
		if(keysDown.contains(KeyEvent.VK_X)){
			scene.getCurrentCamera().setEye(scene.getCurrentCamera().getEye().add(scene.getCurrentCamera().getV().scale(-0.1F)));
			scene.getCurrentCamera().setLookat(scene.getCurrentCamera().getLookat().add(scene.getCurrentCamera().getV().scale(-0.1F)));
		}
		drawPixels();
	}
}

@Override
public void keyReleased(KeyEvent e) {
	keysDown.remove(new Integer(e.getKeyCode()));
}

@Override
public void keyTyped(KeyEvent arg0) {}

@Override
public void mouseDragged(MouseEvent e) {
	
	if(e.getID() == MouseEvent.MOUSE_DRAGGED){
		if(oldX != 0 && oldY != 0){
			int dx = oldX - e.getX(), dy = oldY - e.getY();
			scene.getCurrentCamera().setLookat(scene.getCurrentCamera().getLookat().add(scene.getCurrentCamera().getV().scale(dy * 0.01f)));
			scene.getCurrentCamera().setLookat(scene.getCurrentCamera().getLookat().add(scene.getCurrentCamera().getU().scale(dx * -0.01f)));
		}
		oldX = e.getX();
		oldY = e.getY();
		drawPixels();
	}
}

@Override
public void mouseMoved(MouseEvent e) {}
@Override
public void focusGained(FocusEvent arg0) {}

@Override
public void focusLost(FocusEvent arg0) {
	keysDown.clear();
}
}
